package com.kaihong.template.opinterface;


import com.kaihong.template.utils.Constants;
import com.kaihong.template.utils.Logger;
import com.kaihong.template.utils.PermissionManager;
import com.kaihong.template.utils.TextUtils;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.ace.ability.AceAbility;
import ohos.ace.ability.AceInternalAbility;
import ohos.agp.utils.TextTool;
import ohos.app.AbilityContext;
import ohos.bundle.IBundleManager;
import ohos.data.DatabaseHelper;
import ohos.data.preferences.Preferences;
import ohos.net.NetHandle;
import ohos.net.NetStatusCallback;
import ohos.rpc.IRemoteObject;
import ohos.rpc.MessageOption;
import ohos.rpc.MessageParcel;
import ohos.rpc.RemoteException;
import ohos.utils.net.Uri;
import ohos.utils.zson.ZSONObject;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;


import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;


/**
 * The application context capability interface is implemented on the Java side and can be remotely
 * invoked by CommonOperator.js. This class is in singleton mode and can be registered for multiple times.
 *
 * @since 2021-08-12
 */
public class CommonOperatorAbility extends AceInternalAbility {
    private static final String KEY_UNIQUE_ID = "uniqueId";

    private static volatile CommonOperatorAbility sInstance;

    private final PermissionManager permissionManager;

    private final Set<IRemoteObject> permissionStateCallbackSet = new HashSet<>();

    private final Set<IRemoteObject> permissionRequestCallbackSet = new HashSet<>();

    private final Set<IRemoteObject> startAbilityCallbackSet = new HashSet<>();

    private final Set<IRemoteObject> checkPackageCallbackSet = new HashSet<>();

    private final Set<IRemoteObject> getUniqueIdCallbackSet = new HashSet<>();

    private final Set<IRemoteObject> getVersionCallbackSet = new HashSet<>();
    private final Set<IRemoteObject> downloadFileCallbackSet = new HashSet<>();
    private final Set<IRemoteObject> getOtaFileCallbackSet = new HashSet<>();

    private AbilityContext context;

    private String uniqueId;

    private CommonOperatorAbility(AbilityContext abilityContext) {
        super(Constants.TEMPLATE_BUNDLE_NAME, CommonOperatorAbility.class.getName());
        permissionManager = new PermissionManager(abilityContext);
    }

    /**
     * get the instance of CommonOperatorAbility
     *
     * @return instance
     */
    public static CommonOperatorAbility getInstance() {
        return sInstance;
    }

    /**
     * register the context and init CommonOperatorAbility
     *
     * @param abilityContext ability context
     */
    public static void register(AbilityContext abilityContext) {
        if (sInstance == null) {
            sInstance = new CommonOperatorAbility(abilityContext);
        }
        sInstance.onRegister(abilityContext);
    }

    private void onRegister(AbilityContext abilityContext) {
        context = abilityContext;
        final String filename = "common_db";
        Preferences preferences;
        DatabaseHelper databaseHelper;
        databaseHelper = new DatabaseHelper(context);
        preferences = databaseHelper.getPreferences(filename);
        if (preferences != null) {
            uniqueId = preferences.getString(KEY_UNIQUE_ID, "");
            Logger.i("onRegister uniqueId from sp= " + this.uniqueId);
            if (uniqueId == null || uniqueId.isEmpty()) {
                uniqueId = UUID.randomUUID().toString().replace("-", "").toLowerCase(Locale.ROOT);
                preferences.putString(KEY_UNIQUE_ID, uniqueId);
                preferences.flush();
                Logger.i("onRegister uniqueId from uuid= " + this.uniqueId);
            }
            Logger.i("onRegister uniqueId = " + this.uniqueId);

            setInternalAbilityHandler(this::onRemoteRequest);
        }
    }

    private boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
        switch (code) {
            case OperatorCode.CHECK_PERMISSION:
                checkPermission(data);
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.REQUEST_PERMISSION:
                requestPermission(data);
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.START_ABILITY:
                Logger.i("start ability");
                if (data == null) {
                    return false;
                }
                startAbilityCallbackSet.clear();
                startAbilityCallbackSet.add(data.readRemoteObject());
                startAbility(data);

                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.START_SYSTEM_ABILITY:
                Logger.i("start system ability");
                startAbilityCallbackSet.clear();
                startAbilityCallbackSet.add(data.readRemoteObject());
                startSystemAbility(data);

                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.CHECK_PACKAGE_INSTALLATION:
                checkPackageInstalled(data);
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.OPEN_URL:
                openUrl(data);
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.GET_UNIQUE_ID:
                getUniqueId(data);
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.GET_OTA_VERSION:
                requestFirmwareVersionInfo(data);
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.GET_OTA_FILE:
                getLocalFile(data, context.getFilesDir().getAbsolutePath() + "/update.bin");
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            case OperatorCode.DOWNLOAD_OTA_FILE:
                try {
                    downLoadFromUrl(data, context.getFilesDir().getAbsolutePath() + "/update.bin");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (!replyResult(reply, option, OperatorCode.SUCCESS, new HashMap<>())) {
                    return false;
                }
                break;
            default:
                defaultHandle(reply);
                return false;
        }
        return true;
    }

    private void defaultHandle(MessageParcel reply) {
        Map<String, Object> resultMap = new HashMap<>();
        reply.writeString(ZSONObject.toZSONString(resultMap));
    }

    private boolean replyResult(MessageParcel reply, MessageOption option, int code, Map<String, Object> resultMap) {
        if (option.getFlags() == MessageOption.TF_SYNC) {
            // SYNC
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("code", code);
            dataMap.put("data", resultMap);
            reply.writeString(ZSONObject.toZSONString(dataMap));
        } else {
            // ASYNC
            MessageParcel responseData = MessageParcel.obtain();
            responseData.writeString(ZSONObject.toZSONString(resultMap));
            IRemoteObject remoteReply = reply.readRemoteObject();
            try {
                remoteReply.sendRequest(code, responseData, MessageParcel.obtain(), new MessageOption());
                responseData.reclaim();
            } catch (RemoteException exception) {
                return false;
            }
        }
        return true;
    }

    private void startAbility(MessageParcel data) {
        if (data == null) {
            return;
        }
        String dataString = data.readString();
        Logger.i("startAbility dataString:" + dataString);
        if (TextTool.isNullOrEmpty(dataString)) {
            return;
        }
        ZSONObject dataObject = ZSONObject.stringToZSON(dataString);
        String bundleName = dataObject.getString("bundleName");
        String abilityName = dataObject.getString("abilityName");
        ZSONObject params = dataObject.getZSONObject("params");
        ZSONObject options = dataObject.getZSONObject("options");
        if (params == null) {
            params = new ZSONObject();
        }
        if (options == null) {
            options = new ZSONObject();
        }
        Intent intent = new Intent();
        setIntentOperation(bundleName, abilityName, params, options, intent);
        int flags = 0;
        if (options.containsKey("flags")) {
            flags = options.getIntValue("flags");
            intent.addFlags(Intent.FLAG_ABILITY_NEW_MISSION);
        }
        if (options.containsKey("action")) {
            String action = options.getString("action");
            intent.setAction(action);
        }
        if (options.containsKey("uri")) {
            String uri = options.getString("uri");
            intent.setUri(Uri.parse(uri));
        }
        if ((flags & Intent.FLAG_NOT_OHOS_COMPONENT) == Intent.FLAG_NOT_OHOS_COMPONENT) {
            if (context instanceof AceAbility) {
                ((AceAbility) context).startAbility(intent);
            } else if (context instanceof Ability) {
                ((Ability) context).startAbility(intent);
            } else {
                context.startAbility(intent, 0);
            }
        } else {
            context.startAbility(intent, 0);
        }
        sendReturnRequest(abilityName);
    }

    private void startSystemAbility(MessageParcel data) {
        if (data == null) {
            return;
        }
        String dataString = data.readString();
        Logger.i("startAbility dataString:" + dataString);
        if (TextTool.isNullOrEmpty(dataString)) {
            return;
        }
        ZSONObject dataObject = ZSONObject.stringToZSON(dataString);
        String bundleName = dataObject.getString("bundleName");
        String abilityName = dataObject.getString("abilityName");
        ZSONObject params = dataObject.getZSONObject("params");
        ZSONObject options = dataObject.getZSONObject("options");
        if (options == null) {
            options = new ZSONObject();
        }
        Intent intent = new Intent();
        if (!options.containsKey("action") || !options.containsKey("uri")) {
            return;
        }
        Operation operation = new Intent.OperationBuilder()
                .withAction(options.getString("action"))
                .withUri(Uri.parse(options.getString("uri")))
                .withFlags(2)
                .build();
        intent.setOperation(operation);
        // 启动Ability
        context.startAbility(intent, 0);
        sendReturnRequest(abilityName);
    }

    private void setIntentOperation(
            String bundleName, String abilityName, ZSONObject params, ZSONObject options, Intent intent) {
        intent.setParam("params", params.toString());
        Operation operation;
        if (options != null && options.containsKey("action")) {
            // Start ability by action
            operation = new Intent.OperationBuilder().withDeviceId("").withAction(options.getString("action")).build();
        } else {
            operation =
                    new Intent.OperationBuilder()
                            .withDeviceId("")
                            .withBundleName(bundleName)
                            .withAbilityName(abilityName)
                            .build();
        }
        intent.setOperation(operation);
    }


    private void checkPermission(MessageParcel data) {
        if (data != null) {
            permissionStateCallbackSet.clear();
            permissionStateCallbackSet.add(data.readRemoteObject());
            String dataString = data.readString();
            Logger.i("checkPermission dataString:" + dataString);
            if (TextUtils.isEmpty(dataString)) {
                return;
            }
            ZSONObject dataObject = ZSONObject.stringToZSON(dataString);
            List<String> permissions = dataObject.getZSONArray("permissions").toJavaList(String.class);
            permissionManager.checkPermissions(
                    permissions,
                    (permissionRequested, permissionGranted, canRequestPermissions) -> {
                        Logger.i("Requested permissions:" + ZSONObject.toZSONString(permissionRequested));
                        Logger.i("Granted permissions:" + ZSONObject.toZSONString(permissionGranted));
                        Logger.i("Can Request permissions:" + ZSONObject.toZSONString(canRequestPermissions));
                        Map<String, Object> resultMap = new HashMap<>();
                        resultMap.put("requested", permissionRequested);
                        resultMap.put("granted", permissionGranted);
                        resultMap.put("canRequest", canRequestPermissions);
                        MessageParcel returnData = MessageParcel.obtain();
                        returnData.writeString(ZSONObject.toZSONString(resultMap));
                        MessageParcel reply = MessageParcel.obtain();
                        MessageOption option = new MessageOption();
                        sendRemoteRequest(permissionStateCallbackSet, returnData, reply, option);
                        reply.reclaim();
                        returnData.reclaim();
                    });
        }
    }

    private void requestPermission(MessageParcel data) {
        if (data != null) {
            permissionRequestCallbackSet.clear();
            permissionRequestCallbackSet.add(data.readRemoteObject());
            String dataString = data.readString();
            Logger.i("requestPermission dataString:" + dataString);
            if (TextUtils.isEmpty(dataString)) {
                return;
            }
            ZSONObject dataObject = ZSONObject.stringToZSON(dataString);
            List<String> permissions = dataObject.getZSONArray("permissions").toJavaList(String.class);
            int requestCode = dataObject.getIntValue("requestCode");
            Logger.i("RequestCode:" + requestCode);
            permissionManager.requestPermissions(
                    permissions,
                    requestCode,
                    (permissionRequested, permissionGranted, canRequestPermissions) -> {
                        Logger.i("Requested code:" + requestCode);
                        Logger.i("Requested permissions:" + ZSONObject.toZSONString(permissionRequested));
                        Logger.i("Granted permissions:" + ZSONObject.toZSONString(permissionGranted));
                        Logger.i("Can Request permissions:" + ZSONObject.toZSONString(canRequestPermissions));
                        Map<String, Object> resultMap = new HashMap<>();
                        resultMap.put("requested", permissionRequested);
                        resultMap.put("granted", permissionGranted);
                        resultMap.put("canRequest", canRequestPermissions);
                        resultMap.put("requestCode", requestCode);
                        MessageParcel returnData = MessageParcel.obtain();
                        returnData.writeString(ZSONObject.toZSONString(resultMap));
                        MessageParcel reply = MessageParcel.obtain();
                        MessageOption option = new MessageOption();
                        sendRemoteRequest(permissionRequestCallbackSet, returnData, reply, option);
                        reply.reclaim();
                        returnData.reclaim();
                    });
        }
    }

    private void sendReturnRequest(String abilityName) {
        MessageParcel returnData = MessageParcel.obtain();
        MessageParcel reply = MessageParcel.obtain();
        MessageOption option = new MessageOption();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("started", abilityName);
        returnData.writeString(ZSONObject.toZSONString(resultMap));
        sendRemoteRequest(startAbilityCallbackSet, returnData, reply, option);
        reply.reclaim();
        returnData.reclaim();
    }

    private void checkPackageInstalled(MessageParcel data) {
        if (data == null) {
            return;
        }
        checkPackageCallbackSet.clear();
        checkPackageCallbackSet.add(data.readRemoteObject());
        String dataString = data.readString();
        Logger.i("checkPackageInstalled dataString:" + dataString);
        if (TextTool.isNullOrEmpty(dataString)) {
            return;
        }
        ZSONObject dataObject = ZSONObject.stringToZSON(dataString);
        if (dataObject == null) {
            return;
        }
        String packageName = dataObject.getString("bundleName");
        boolean isResult = false;
        IBundleManager manager = context.getBundleManager();
        try {
            boolean isApplicationEnabled = manager.isApplicationEnabled(packageName);
            if (isApplicationEnabled) {
                isResult = true;
            }
        } catch (IllegalArgumentException e) {
            Logger.i("Package " + packageName + " is not installed!");
        }
        Logger.i("Check package " + packageName + " installation result is " + isResult);
        MessageParcel returnData = MessageParcel.obtain();
        MessageParcel reply = MessageParcel.obtain();
        MessageOption option = new MessageOption();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("installed", isResult);
        returnData.writeString(ZSONObject.toZSONString(resultMap));
        sendRemoteRequest(checkPackageCallbackSet, returnData, reply, option);
        reply.reclaim();
        returnData.reclaim();
    }

    private void openUrl(MessageParcel data) {
        if (data == null) {
            return;
        }
        checkPackageCallbackSet.clear();
        checkPackageCallbackSet.add(data.readRemoteObject());
        String dataString = data.readString();
        Logger.i("openUrl dataString:" + dataString);
        if (TextTool.isNullOrEmpty(dataString)) {
            return;
        }
        ZSONObject dataObject = ZSONObject.stringToZSON(dataString);
        String url = dataObject.getString("url");
        openWebView(url);
        Logger.i("Start url success, Url: " + url);
        MessageParcel returnData = MessageParcel.obtain();
        MessageParcel reply = MessageParcel.obtain();
        MessageOption option = new MessageOption();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("succeed", true);
        returnData.writeString(ZSONObject.toZSONString(resultMap));
        sendRemoteRequest(checkPackageCallbackSet, returnData, reply, option);
        reply.reclaim();
        returnData.reclaim();
    }

    private void openWebView(String url) {
        Intent intent = new Intent();
        Operation operation =
                new Intent.OperationBuilder()
                        .withAction("android.intent.action.VIEW")
                        .withFlags(Intent.FLAG_NOT_OHOS_COMPONENT)
                        .build();
        intent.setOperation(operation);
        intent.setUri(Uri.parse(url));
        context.startAbility(intent, 0);
    }

    private void getUniqueId(MessageParcel data) {
        if (data == null) {
            return;
        }
        getUniqueIdCallbackSet.clear();
        getUniqueIdCallbackSet.add(data.readRemoteObject());
        Logger.i("get unique id = " + uniqueId);
        MessageParcel returnData = MessageParcel.obtain();
        MessageParcel reply = MessageParcel.obtain();
        MessageOption option = new MessageOption();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put(KEY_UNIQUE_ID, uniqueId);
        returnData.writeString(ZSONObject.toZSONString(resultMap));
        sendRemoteRequest(getUniqueIdCallbackSet, returnData, reply, option);
        reply.reclaim();
        returnData.reclaim();
    }

    private void sendRemoteRequest(
            Set<IRemoteObject> callbackSet, MessageParcel returnData, MessageParcel reply, MessageOption option) {
        for (IRemoteObject remoteObject : callbackSet) {
            try {
                remoteObject.sendRequest(0, returnData, reply, option);
            } catch (RemoteException e) {
                Logger.e("RemoteException:" + e);
            }
        }
    }

    /**
     * 请求固件信息
     *
     * @param data
     */
    private void requestFirmwareVersionInfo(MessageParcel data) {
        Logger.i("requestFirmwareVersionInfo");
        if (data == null) {
            Logger.i("requestFirmwareVersionInfo data is null");
            return;
        }
        getVersionCallbackSet.clear();
        getVersionCallbackSet.add(data.readRemoteObject());

//        String url = "http://139.159.252.23:8190/bluetooth/firmwareManagement/queryFirmware?pageSize=10&pageNum=1";
        ZSONObject dataObject = ZSONObject.stringToZSON(data.readString());
        String url = dataObject.getString("url");
        Logger.i("requestFirmwareVersionInfo  url：" + url);
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(url)
                .get().build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Logger.d("GET_OTA_VERSION  onFailure ：" + e);
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                Logger.d("GET_OTA_VERSION  onResponse ：" + response);
                if (response.isSuccessful()) {
                    String jsonstring = response.body().string();
                    // 返回结果当前仅支持String，对于复杂结构可以序列化为ZSON字符串上报
                    MessageParcel returnData = MessageParcel.obtain();
                    MessageParcel reply = MessageParcel.obtain();
                    MessageOption option = new MessageOption();
                    Map<String, Object> resultMap = new HashMap<>();
                    resultMap.put("version", jsonstring);
                    returnData.writeString(ZSONObject.toZSONString(resultMap));
                    sendRemoteRequest(getVersionCallbackSet, returnData, reply, option);
                    reply.reclaim();
                    returnData.reclaim();
                }
            }
        });

    }

    /**
     * 网络状态监听接口g
     */
    private final NetStatusCallback callback = new NetStatusCallback() {
        @Override
        public void onAvailable(NetHandle handle) {
            Logger.i("NetStatusCallback onAvailable");
        }

        @Override
        public void onBlockedStatusChanged(NetHandle handle, boolean blocked) {
            Logger.i("NetStatusCallback onBlockedStatusChanged");
        }
    };

    /**
     * 获取已下载文件字节流
     */
    private void getLocalFile(MessageParcel data, String filePath) {
        Logger.d("getLocalFile ");
        getOtaFileCallbackSet.clear();
        getOtaFileCallbackSet.add(data.readRemoteObject());
        try {
            FileInputStream fileInputStream = new FileInputStream(filePath);
            String fileStream = BinaryToHexString(readInputStream(fileInputStream));
            Logger.d("getLocalFile  fileInputStream  fileStream.size" + fileStream.length());
            /**
             * 为了解决js接受参数大小受限问题，将文件流分包发送，每包10k~30000长度
             */
            List<String> arrayStream = getStrList(fileStream, 30000);
            int size = arrayStream.size();
            for (int i = 0; i < size; i++) {
                MessageParcel returnData = MessageParcel.obtain();
                MessageParcel reply = MessageParcel.obtain();
                MessageOption option = new MessageOption();
                Map<String, Object> resultMap = new HashMap<>();
                if (i == 0) {
                    resultMap.put("fileCode", 1);
                }
                resultMap.put("fileData", arrayStream.get(i));
                if (i == size - 1 || size == 1) {
                    resultMap.put("fileCode", 2);
                }
                Logger.d("getLocalFile  for fileData i=" + i + "  data:" + resultMap.get("fileData"));
                returnData.writeString(ZSONObject.toZSONString(resultMap));
                Logger.d("getLocalFile   returnData =" + returnData.readString());
                sendRemoteRequest(getOtaFileCallbackSet, returnData, reply, option);
                reply.reclaim();
                returnData.reclaim();
            }
            if (fileInputStream != null) {
                fileInputStream.close();
            }
        } catch (FileNotFoundException e) {
            Logger.e("getLocalFile  failure ");
            e.printStackTrace();
        } catch (IOException e) {
            Logger.e("getLocalFile IOException  failure ");
            e.printStackTrace();
        }
    }

    /**
     * 从网络Url中下载文件
     *
     * @param data        js端传输url地址
     * @param newFilePath 新保存文件地址
     * @throws IOException
     */
    public void downLoadFromUrl(MessageParcel data, String newFilePath) throws IOException {
        Logger.i("downLoadFromUrl fileName:" + newFilePath);
        if (data == null) {
            return;
        }
        downloadFileCallbackSet.clear();
        downloadFileCallbackSet.add(data.readRemoteObject());
        ZSONObject dataObject = ZSONObject.stringToZSON(data.readString());
        URL url = new URL(dataObject.getString("url"));
        Logger.i("downLoadFromUrl fileName   url:" + url);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时间为3秒
        conn.setConnectTimeout(3 * 3000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //得到输入流
        InputStream inputStream = conn.getInputStream();

        //获取自己数组
        byte[] getData = readInputStream(inputStream);

        //文件保存位置
        File saveDir = new File(newFilePath);
        if (saveDir.exists()) {
            Logger.i("downLoadFromUrl newFilePath is exist so delete:");
            saveDir.delete();
        }
        FileOutputStream outputStream = new FileOutputStream(saveDir);
        outputStream.write(getData);
        if (outputStream != null) {
            outputStream.close();
        }
        if (inputStream != null) {
            inputStream.close();
        }
        MessageParcel returnData = MessageParcel.obtain();
        MessageParcel reply = MessageParcel.obtain();
        MessageOption option = new MessageOption();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("downLoadResult", newFilePath);
        returnData.writeString(ZSONObject.toZSONString(resultMap));
        sendRemoteRequest(downloadFileCallbackSet, returnData, reply, option);
        reply.reclaim();
        returnData.reclaim();
        Logger.i("downLoadFromUrl download success");
    }

    /**
     * 从输入流中获取字节数组
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    private static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    private static final String hexStr = "0123456789ABCDEF";

    /**
     * @param bytes 源二进制字节数组
     * @return 将二进制转换为十六进制字符输出
     */
    public static String BinaryToHexString(byte[] bytes) {

        String result = "";
        String hex = "";
        for (int i = 0; i < bytes.length; i++) {
            //字节高4位
            hex = String.valueOf(hexStr.charAt((bytes[i] & 0xF0) >> 4));
            //字节低4位
            hex += String.valueOf(hexStr.charAt(bytes[i] & 0x0F));
            result += hex + " ";
        }
        return result;
    }

    /**
     * 把原始字符串分割成指定长度的字符串列表
     *
     * @param inputString 原始字符串
     * @param length      指定长度
     * @return
     */
    private static List<String> getStrList(String inputString, int length) {
        int size = inputString.length() / length;
        if (inputString.length() % length != 0) {
            size += 1;
        }
        return getStrList(inputString, length, size);
    }

    /**
     * 把原始字符串分割成指定长度的字符串列表
     *
     * @param inputString 原始字符串
     * @param length      指定长度
     * @param size        指定列表大小
     * @return
     */
    private static List<String> getStrList(String inputString, int length,
                                           int size) {
        List<String> list = new ArrayList<String>();
        for (int index = 0; index < size; index++) {
            String childStr = substring(inputString, index * length,
                    (index + 1) * length);
            list.add(childStr);
        }
        return list;
    }

    /**
     * 分割字符串，如果开始位置大于字符串长度，返回空
     *
     * @param str 原始字符串
     * @param f   开始位置
     * @param t   结束位置
     * @return
     */
    private static String substring(String str, int f, int t) {
        if (f > str.length())
            return null;
        if (t > str.length()) {
            return str.substring(f, str.length());
        } else {
            return str.substring(f, t);
        }
    }

    /**
     * unregister the CommonOperatorAbility
     */
    public static void unregister() {
        if (sInstance != null) {
            sInstance.onUnregister();
        }
    }

    private void onUnregister() {
        context = null;
        setInternalAbilityHandler(null);
    }

    /**
     * set context
     *
     * @param context context
     */
    public void setContext(AbilityContext context) {
        this.context = context;
        permissionManager.setContext(context);
    }

    /**
     * get the permission manager
     *
     * @return PermissionManager
     */
    public PermissionManager getPermissionManager() {
        return permissionManager;
    }
}
